import { Shaders } from "./shaders"
import { BitMath } from "../math/math";
import { Render_Texture } from "./render_texture";
import { ShaderResources } from "./shaderress";

//简化设计，不要搞幺蛾子
export namespace Renderer {



    export class RenderWebgl {
        readonly webgl: WebGLRenderingContext;
        worldPalette: Uint8Array;
        worldLUT: Uint8Array;
        get worldMatrix(): Float32Array {
            if (this.target != null)
                return this.target.worldMatrix;
            else
                return this.target_Canvas.worldMatrix;
        }
        worldTexPalette: Render_Texture.Texture;
        worldTexLUT: Render_Texture.Texture;
        target: Render_Texture.ITarget | null;
        target_Canvas: Render_Texture.CanvasScreen;


        constructor(webgl: WebGLRenderingContext) {
            this.webgl = webgl;
            this.worldPalette = new Uint8Array(1024);//4*256
            this.worldLUT = new Uint8Array(65536);//使用565色表，因为565色表只需要64k，而888色表需要16MB
            //565色标当然会有一点点台阶，但我们用的调色板模式，本来就存在巨大的台阶，这根本不是问题
            this.worldTexPalette = new Render_Texture.Texture(webgl, Render_Texture.TextureFormat.RGBA);
            //this.worldTexPalette.loadimgFromData(null, 256, 1, false, false);
            this.worldTexLUT = new Render_Texture.Texture(webgl, Render_Texture.TextureFormat.GRAY);

            let _palette = BitMath.Palette.CreateDefault();
            RenderWebgl.ParsePalette(_palette, this.worldPalette, this.worldTexPalette);
            //RenderWebgl.ParseLUTLAB(_palette, this.worldLUT, this.worldTexLUT);
            this.target_Canvas = new Render_Texture.CanvasScreen(this.webgl.canvas);

            this.target = null;



            ShaderResources.InitShader(this.webgl);
        }
        static ParsePalette(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture): void {
            var labs = [];
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                targetbuf[i * 4 + 0] = c.r;
                targetbuf[i * 4 + 1] = c.g
                targetbuf[i * 4 + 2] = c.b;
                targetbuf[i * 4 + 3] = c.a;
                let clab = new BitMath.ColorLab();
                clab.FromRGB(c.r / 255, c.g / 255, c.b / 255);
                labs.push(clab);
            }
            targettex.loadimgFromData(targetbuf, 256, 1, false, false);
        }
        static ParseLUTRGB(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture): void {
            var labs = [];
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                //let clab = new BitMath.ColorLab();
                //clab.FromRGB(c.r / 255, c.g / 255, c.b / 255);
                labs.push(c);
            }
            for (var b = 0; b < 32; b++) {
                for (var r = 0; r < 32; r++) {
                    for (var g = 0; g < 64; g++) {
                        let index = (g << 10) | (r << 5) | (b);

                        let clab = new BitMath.ColorLab();
                        clab.FromRGB(r / 31, g / 63, b / 31);
                        //使用clab空间生成lut
                        //这还不是最好的， 回头试验CIEDE2000

                        var c = new BitMath.Color8Bit_RGB();
                        c.r = r * 8;
                        c.g = g * 4;
                        c.b = b * 8;

                        let colorindex = 1;
                        let dist = Number.MAX_VALUE;
                        for (var i = 1; i < 256; i++) {
                            //let cpal = labs[i];
                            //let _dist = BitMath.ColorLab.QuickDist(clab, cpal);
                            let cpal = palette.GetColorRGB(i);
                            let _dist = BitMath.Color8Bit_RGB.QuickDist(c, cpal);
                            if (_dist < dist) {
                                colorindex = i;
                                dist = _dist;
                            }
                        }

                        targetbuf[index] = colorindex;
                    }
                }
            }
            targettex.loadimgFromData(targetbuf, 1024, 64, false, false);
        }
        static ParseLUTLAB(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture): void {
            var labs = [];
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                let clab = new BitMath.ColorLab();
                clab.FromRGB(c.r / 255, c.g / 255, c.b / 255);
                labs.push(clab);
            }
            for (var b = 0; b < 32; b++) {
                for (var r = 0; r < 32; r++) {
                    for (var g = 0; g < 64; g++) {
                        let index = (g << 10) | (r << 5) | (b);

                        let clab = new BitMath.ColorLab();
                        clab.FromRGB(r / 31, g / 63, b / 31);
                        //使用clab空间生成lut
                        //这还不是最好的， 回头试验CIEDE2000

                        // var c = new BitMath.Color8Bit_RGB();
                        // c.r = r * 8;
                        // c.g = g * 4;
                        // c.b = b * 8;

                        let colorindex = 1;
                        let dist = Number.MAX_VALUE;
                        for (var i = 1; i < 256; i++) {
                            let cpal = labs[i];
                            let _dist = BitMath.ColorLab.QuickDist(clab, cpal);
                            // let cpal =   palette.GetColorRGB(i);
                            // let _dist = BitMath.Color8Bit_RGB.QuickDist(c, cpal);
                            if (_dist < dist) {
                                colorindex = i;
                                dist = _dist;
                            }
                        }

                        targetbuf[index] = colorindex;
                    }
                }
            }
            targettex.loadimgFromData(targetbuf, 1024, 64, false, false);
        }

        static ParseLUTLABQuick_Worker(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture) {
            var ubd = new Uint8Array(256 * 3);
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                ubd[i * 3 + 0] = c.r;
                ubd[i * 3 + 1] = c.g
                ubd[i * 3 + 2] = c.b;
            }
            let worker = new Worker("lib.letusbit/worker/lut.js");
            worker.onmessage = (e) => {
                if (typeof e.data == 'string') {
                    console.warn(e.data);
                }
                targetbuf = e.data as Uint8Array;
                targettex.loadimgFromData(targetbuf, 1024, 64, false, false);
                worker.terminate();
            }
            worker.postMessage({ "type": "lutquick_index", "pal": ubd });

        }
        static ParseLUTLABSlow_Worker(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture) {
            var ubd = new Uint8Array(256 * 3);
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                ubd[i * 3 + 0] = c.r;
                ubd[i * 3 + 1] = c.g
                ubd[i * 3 + 2] = c.b;
            }
            let worker = new Worker("lib.letusbit/worker/lut.js");
            worker.onmessage = (e) => {
                if (typeof e.data == 'string') {
                    console.warn(e.data);
                }
                targetbuf = e.data as Uint8Array;
                targettex.loadimgFromData(targetbuf, 1024, 64, false, false);
                worker.terminate();
            }
            worker.postMessage({ "type": "lut_index", "pal": ubd });

        }
        static ParseLUTLABSLow(palette: BitMath.Palette, targetbuf: Uint8Array, targettex: Render_Texture.Texture): void {
            var labs = [];
            for (var i = 0; i < 256; i++) {
                let c = palette.GetColorRGB(i);
                let clab = new BitMath.ColorLab();
                clab.FromRGB(c.r / 255, c.g / 255, c.b / 255);
                labs.push(clab);
            }
            for (var b = 0; b < 32; b++) {
                for (var r = 0; r < 32; r++) {
                    for (var g = 0; g < 64; g++) {
                        let index = (g << 10) | (r << 5) | (b);

                        let clab = new BitMath.ColorLab();
                        clab.FromRGB(r / 31, g / 63, b / 31);
                        //使用clab空间生成lut
                        //这还不是最好的， 回头试验CIEDE2000

                        // var c = new BitMath.Color8Bit_RGB();
                        // c.r = r * 8;
                        // c.g = g * 4;
                        // c.b = b * 8;

                        let colorindex = 1;
                        let dist = Number.MAX_VALUE;
                        for (var i = 1; i < 256; i++) {
                            let cpal = labs[i];
                            let _dist = BitMath.ColorLab.ciede2000(clab, cpal);
                            // let cpal =   palette.GetColorRGB(i);
                            // let _dist = BitMath.Color8Bit_RGB.QuickDist(c, cpal);
                            if (_dist < dist) {
                                colorindex = i;
                                dist = _dist;
                            }
                        }

                        targetbuf[index] = colorindex;
                    }
                }
            }
            targettex.loadimgFromData(targetbuf, 1024, 64, false, false);
        }
        Begin(): void {
            this.target = null;
            this.target_Canvas.Begin(this.webgl);
        }

        End(): void {

            this.target_Canvas.End(this.webgl);
        }

        BeginTarget(target: Render_Texture.ITarget): void {
            this.target = target;
            this.target.Begin(this.webgl);
        }
        EndTarget(): void {
            this.target?.End(this.webgl);
            this.target = null;
        }


        CreateBatcher(): SpriteBatcher {
            return new SpriteBatcher(this);
        }
        CreateMesh(): Mesh {
            return new Mesh(this);
        }
    }
    export interface IRenderer {
        Render(): void;
    }
    export class Mesh implements IRenderer {
        constructor(render: RenderWebgl) {

        }
        Render(): void {

        }
    }

    export class SpritePoint {
        x: number = 0;
        y: number = 0;
        z: number = 0;

        r: number = 0;
        g: number = 0;
        b: number = 0;
        a: number = 0;

        u: number = 0;
        v: number = 0;
    }

    export class Material {
        shader?: Shaders.ShaderProgram;
        ztest: boolean = false;
        zwrite: boolean = false;
        transparent: boolean = true;
        texMain?: Render_Texture.ITexture;
        uniforms: { [id: string]: any } = {};
    }
    const batcherbufsize: number = 4096;
    export class SpriteBatcher implements IRenderer {
        render: RenderWebgl;
        vbo: WebGLBuffer;
        array: Float32Array = new Float32Array(batcherbufsize * 9);//ness
        dataseek: number = 0;
        mat: Material | null = null;
        texWhite: Render_Texture.Texture;;
        constructor(render: RenderWebgl) {
            this.render = render;
            let webgl = render.webgl;
            var _vbo = webgl.createBuffer();
            if (_vbo == null) {
                throw "error vbo create.";
            }
            this.vbo = _vbo;
            this.texWhite = new Render_Texture.Texture(webgl, Render_Texture.TextureFormat.RGBA);
            var _data = new Uint8Array(16);
            for (var i = 0; i < _data.length; i++)
                _data[i] = 255;
            this.texWhite.loadimgFromData(_data, 2, 2, false, false);
        }
        Init(webgl: WebGLRenderingContext, program: Shaders.ShaderProgram): void {


        }
        SetMat(mat: Material): void {
            if (mat == this.mat) return;
            this.Render();
            let webgl = this.render.webgl;
            webgl.disable(webgl.CULL_FACE);
            this.mat = mat;

            //指定shader和vbo

            //关于深度 ，跟着spritebatcher走
            if (mat.zwrite) {
                webgl.depthMask(true);//这是zwrite
            }
            else {
                webgl.depthMask(false);//这是zwrite
            }


            if (mat.ztest) {
                webgl.enable(webgl.DEPTH_TEST);//这是ztest
                webgl.depthFunc(webgl.LEQUAL);//这是ztest方法
            }
            else {
                webgl.disable(webgl.DEPTH_TEST);//这是ztest
            }

            if (this.mat.transparent) {
                //alphablend ，跟着mat走
                webgl.enable(webgl.BLEND);
                webgl.blendEquation(webgl.FUNC_ADD);
                //this.webgl.blendFunc(this.webgl.ONE, this.webgl.ONE_MINUS_SRC_ALPHA);
                webgl.blendFuncSeparate(webgl.ONE, webgl.ONE_MINUS_SRC_ALPHA,
                    webgl.SRC_ALPHA, webgl.ONE);
            }
            else {
                webgl.disable(webgl.BLEND);
            }

            let shader = mat.shader;
            if (shader == undefined)
                return;
            let program = shader.program;
            if (program != undefined) {
                webgl.useProgram(program);
            }
            else {
                webgl.useProgram(null);
            }
            webgl.bindBuffer(webgl.ARRAY_BUFFER, this.vbo);


            //指定固定的数据结构，然后根据存在program的数据去绑定咯。

            //绑定vbo和shader顶点格式，这部分应该要区分材质改变与参数改变，可以少切换一些状态
            if (shader.attrIndexPos >= 0) {
                webgl.enableVertexAttribArray(shader.attrIndexPos);
                //36 是数据步长(字节)，就是数据结构的长度
                //0 是数据偏移（字节）
                webgl.vertexAttribPointer(shader.attrIndexPos, 3, webgl.FLOAT, false, 36, 0);
            }
            if (shader.attrIndexColor >= 0) {
                webgl.enableVertexAttribArray(shader.attrIndexColor);
                webgl.vertexAttribPointer(shader.attrIndexColor, 4, webgl.FLOAT, false, 36, 12);
            }
            if (shader.attrIndexUV >= 0) {
                webgl.enableVertexAttribArray(shader.attrIndexUV);
                webgl.vertexAttribPointer(shader.attrIndexUV, 2, webgl.FLOAT, false, 36, 28);
            }

            if (shader.uniMatrix != null) {
                webgl.uniformMatrix4fv(shader.uniMatrix, false, this.render.worldMatrix);
            }
            if (shader.uniTexMain != null) {
                webgl.activeTexture(webgl.TEXTURE0);
                let tex = this.mat.texMain;
                if (tex?.texture != undefined) {
                    webgl.bindTexture(webgl.TEXTURE_2D, tex.texture);
                }
                else {
                    webgl.bindTexture(webgl.TEXTURE_2D, this.texWhite.texture);
                }
                webgl.uniform1i(shader.uniTexMain, 0);
                //console.log("settex");
            }
            if (shader.uniTexPalette != null) {
                webgl.activeTexture(webgl.TEXTURE1);
                let tex = this.render.worldTexPalette;
                webgl.bindTexture(webgl.TEXTURE_2D, tex.texture);
                webgl.uniform1i(shader.uniTexPalette, 1);
            }
            if (shader.uniTexLut != null) {
                webgl.activeTexture(webgl.TEXTURE2);
                let tex = this.render.worldTexLUT;
                webgl.bindTexture(webgl.TEXTURE_2D, tex.texture);
                webgl.uniform1i(shader.uniTexLut, 2);
            }
            let texpos = 3;
            for (var key in shader.uniforms) {
                if (texpos >= 3)
                    webgl.activeTexture(webgl.TEXTURE3);
                let loc = shader.uniforms[key];
                let tex = mat.uniforms[key] as Render_Texture.ITexture;
                webgl.bindTexture(webgl.TEXTURE_2D, tex.texture);
                webgl.uniform1i(loc, texpos);
                texpos++;
            }
        }

        addQuad(ps: SpritePoint[]): void//添加四边形，必须是四的倍数，不接受裁剪
        {
            if (this.mat == undefined || this.mat.shader == undefined) return;

            for (var jc = 0; jc < 6; jc++) {
                var j = jc < 3 ? jc : 6 - jc;// 0->0 1->1 2->2
                // if (j > 2) j = 6 - jc; // 3->3 4->2 5->1

                let i = this.dataseek * 9;

                this.array[i++] = ps[j].x;
                this.array[i++] = ps[j].y;
                this.array[i++] = ps[j].z;
                this.array[i++] = ps[j].r;
                this.array[i++] = ps[j].g;
                this.array[i++] = ps[j].b;
                this.array[i++] = ps[j].a;
                this.array[i++] = ps[j].u;
                this.array[i++] = ps[j].v;

                this.dataseek++;
            }

            if (this.dataseek >= batcherbufsize) {
                this.Render();
            }
        }
        addTri(ps: SpritePoint[]): void//添加三角形，必须是三的倍数 ,三角形不支持硬裁剪
        {
            if (this.mat == undefined || this.mat.shader == undefined) return;

            {
                for (var j = 0; j < 3; j++) {
                    let i = this.dataseek * 9;
                    //for (var e in ps[j])
                    //{
                    //    this.array[i++] = ps[j][e];
                    //}
                    this.array[i++] = ps[j].x;
                    this.array[i++] = ps[j].y;
                    this.array[i++] = ps[j].z;
                    this.array[i++] = ps[j].r;
                    this.array[i++] = ps[j].g;
                    this.array[i++] = ps[j].b;
                    this.array[i++] = ps[j].a;
                    this.array[i++] = ps[j].u;
                    this.array[i++] = ps[j].v;

                    this.dataseek++;
                    //this.data.push(ps[j].x);
                    //this.data.push(ps[j].y);
                    //this.data.push(ps[j].z);
                    //this.data.push(ps[j].r);
                    //this.data.push(ps[j].g);
                    //this.data.push(ps[j].b);
                    //this.data.push(ps[j].a);
                    //this.data.push(ps[j].r);
                    //this.data.push(ps[j].g);
                    //this.data.push(ps[j].b);
                    //this.data.push(ps[j].a);
                    //this.data.push(ps[j].u);
                    //this.data.push(ps[j].v);

                }
            }
            if (this.dataseek >= batcherbufsize) {
                this.Render();
            }

        }

        //这个接口接受裁剪
        addRect(ps: SpritePoint[]): void//添加四边形，必须是四的倍数
        {
            if (this.mat == undefined || this.mat.shader == undefined) return;

            if (this.rectClip != null) //使用裁剪
            {
                var xmin = ps[0].x;
                var xmax = ps[3].x;
                var ymin = ps[0].y;
                var ymax = ps[3].y;
                var umin = ps[0].u
                var umax = ps[3].u;
                var vmin = ps[0].v;
                var vmax = ps[3].v;
                var wsize = xmax - xmin;
                var hsize = ymax - ymin;
                var usize = umax - umin;
                var vsize = vmax - vmin;
                var xl = Math.max(xmin, this.rectClip.x);
                var xr = Math.min(xmax, this.rectClip.x + this.rectClip.w);
                var yt = Math.max(ymin, this.rectClip.y);
                var yb = Math.min(ymax, this.rectClip.y + this.rectClip.h);
                var lf = (xl - xmin) / wsize;
                var tf = (yt - ymin) / hsize;
                var rf = (xr - xmax) / wsize;
                var bf = (yb - ymax) / hsize;
                umin = umin + lf * usize;
                vmin = vmin + tf * vsize;
                umax = umax + rf * usize;
                vmax = vmax + bf * vsize;
                for (var jc = 0; jc < 6; jc++) {
                    var j = jc < 3 ? jc : 6 - jc;// 0->0 1->1 2->2
                    // if (j > 2) j = 6 - jc; // 3->3 4->2 5->1

                    let i = this.dataseek * 13;

                    var x = ps[j].x;
                    if (x < xl) x = xl;
                    if (x > xr) x = xr;
                    var y = ps[j].y;
                    if (y < yt) y = yt;
                    if (y > yb) y = yb;
                    var u = ps[j].u;
                    if (u < umin) u = umin;
                    if (u > umax) u = umax;
                    var v = ps[j].v;
                    if (v < vmin) v = vmin;
                    if (v > vmax) v = vmax;
                    this.array[i++] = x;
                    this.array[i++] = y;
                    this.array[i++] = ps[j].z;
                    this.array[i++] = ps[j].r;
                    this.array[i++] = ps[j].g;
                    this.array[i++] = ps[j].b;
                    this.array[i++] = ps[j].a;
                    this.array[i++] = u;
                    this.array[i++] = v;

                    this.dataseek++;
                }
            }
            else {
                for (var jc = 0; jc < 6; jc++) {
                    var j = jc < 3 ? jc : 6 - jc;// 0->0 1->1 2->2
                    // if (j > 2) j = 6 - jc; // 3->3 4->2 5->1

                    let i = this.dataseek * 9;

                    this.array[i++] = ps[j].x;
                    this.array[i++] = ps[j].y;
                    this.array[i++] = ps[j].z;
                    this.array[i++] = ps[j].r;
                    this.array[i++] = ps[j].g;
                    this.array[i++] = ps[j].b;
                    this.array[i++] = ps[j].a;
                    this.array[i++] = ps[j].u;
                    this.array[i++] = ps[j].v;

                    this.dataseek++;
                }
            }
            if (this.dataseek >= batcherbufsize) {
                this.Render();
            }
        }

        rectClip: BitMath.Rect | null = null;
        setRectClip(rect: BitMath.Rect) {
            this.rectClip = rect;
        }
        closeRectClip() {
            this.rectClip = null;
        }
        Render(): void {

            if (this.dataseek == 0)
                return;

            let webgl = this.render.webgl;
            this.mat = null;
            //填充vbo
            webgl.bindBuffer(webgl.ARRAY_BUFFER, this.vbo);
            webgl.bufferData(webgl.ARRAY_BUFFER, this.array, webgl.DYNAMIC_DRAW);
            //绘制
            webgl.bindBuffer(webgl.ARRAY_BUFFER, this.vbo);
            webgl.drawArrays(webgl.TRIANGLES, 0, this.dataseek);
            //清理状态，可以不干
            //this.webgl.bindBuffer(this.webgl.ARRAY_BUFFER, null);

            //this.data.length = 0;
            this.dataseek = 0;
        }
    }
}